home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PCMania 73
/
PCMania CD73_1.iso
/
sharewar
/
utiles
/
viff
/
viffutil.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-12-18
|
17KB
|
696 lines
/************************* -*- Mode: C -*- *****************************
*
* viffutil.c -- utility functions for viff
*
* Copyright (C) 1996-1997 Richard Flamsholt S0rensen. All rights reserved.
*
* Author : Richard Flamsholt S0rensen
* Created On : Wed Dec 18 16:00:06 1996
* Last Modified By: Richard Flamsholt S0rensen
* Last Modified On: Thu Dec 18 14:08:19 1997
* Update Count : 43
* Revision History: None
*
* COMMENTS
* HISTORY
**********************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#if defined(SCO)
# include <fcntl.h>
# include <unistd.h>
# include <dirent.h>
#elif defined(BC31)
# include <dir.h>
#elif defined(MSVC)
# include <direct.h>
#elif defined(DJGPP)
# include <dirent.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "viff.h"
#define WILD_MAXARGC 5000u
#define WILD_MAXARGV 60000u
#ifndef S_ISDIR
# ifndef S_IFDIR
# error Must be compiled with a compiler that has a decent stat() call
# endif
# define S_ISDIR(mode) ((mode)&S_IFDIR)
#endif
typedef struct {
int len;
char *skipext;
char *ifext;
} GENERATED_FILE;
static GENERATED_FILE generated_files[] = {
{2,".h", ".asn"}, /* ASN.1 generated .h file */
{2,".c", ".asn"}, /* ASN.1 generated .c file */
{2,".h", ".y"}, /* yacc(1) generated .h file */
{2,".c", ".y"}, /* yacc(1) generated .c file */
{2,".i", ".c"}, /* preprocessed .c file */
{2,".elc",".el"}, /* compiled emacs-lisp file */
{0,NULL,NULL}
};
typedef struct {
int len;
char *ext;
} BINARY_EXT;
static BINARY_EXT binary_ext[] = {
#if defined(MSDOS) || defined(WIN32)
{4,".arj"}, /* ARJ archive */
{4,".bmp"}, /* bitmap */
{4,".com"}, /* DOS com-file */
{4,".dll"}, /* Windows DLL */
{4,".exe"}, /* DOS exe-file */
{4,".lib"}, /* library */
{4,".lzh"}, /* LHARC */
{4,".obj"}, /* object file */
{2,".z" }, /* compressed file */
{4,".zip"}, /* PKZIP archive */
{4,".zoo"}, /* zoo archive */
{4,".aps"}, /* MSVC++ binary resource */
{4,".bsc"}, /* MSVC++ browser database */
{4,".clw"}, /* MSVC++ class wizard file */
{4,".mdp"}, /* MSVC++ project workspace file */
{4,".pdb"}, /* MSVC++ program database */
{4,".sbr"}, /* MSVC++ source browser file */
{4,".wsp"}, /* MSVC++ workspace information */
{4,".vcp"}, /* MSVC++ workspace information */
{4,".vcw"}, /* MSVC++ workbench status */
{4,".exp"}, /* MSVC++ export file */
{4,".res"}, /* MSVC++ compiled resource */
{4,".rct"}, /* MSVC++ resource template file */
#endif
#ifdef UNIX
{2,".Z" }, /* Lempel-Ziv compressed file */
{2,".a" }, /* statically linked library */
{3,".gz" }, /* GNU Zip */
{2,".o" }, /* object file */
{4,".out"}, /* a.out */
{3,".so" }, /* dynamically linked library */
{4,".tar"}, /* tar archive */
{4,".uid"}, /* Motif resource file */
#endif
{4,".gif"},
{0,NULL}
};
static char *binary_files[] = {
#ifdef UNIX
"a.out",
"core",
#endif
NULL,
};
static struct {
char **argv;
char *fnames;
} constructed;
static int vmessage(const char *fmt, va_list varg);
static void free_constructed(void);
static BOOLEAN find_diffcmd_do(char *path);
void
error(const char *fmt, ...)
{
va_list varg;
va_start(varg, fmt);
if (curses_on) {
(void)vmessage(fmt, varg);
} else {
fprintf(stderr, "viff: ");
vfprintf(stderr, fmt, varg);
fprintf(stderr, "\n");
}
va_end(varg);
exit(EXIT_FAILURE);
}
void
panic(const char *fmt, ...)
{
va_list varg;
va_start(varg, fmt);
if (curses_on) {
(void)vmessage(fmt, varg);
} else {
fprintf(stderr, "Viff panic: ");
vfprintf(stderr, fmt, varg);
fprintf(stderr, "\n");
}
va_end(varg);
exit(EXIT_FAILURE);
}
void
brief_usage(void)
{
fprintf(stderr, "usage: viff files\n(use viff -h for help)\n");
exit(EXIT_FAILURE);
}
void
extended_usage(void)
{
printf("usage:\n"
" viff options file file\n"
" viff options files directory\n"
" viff options directory files\n"
" viff options directory directory\n"
"options:\n"
" info: -h give this help\n"
" -V show viff version\n"
" viff: -v verbose; report skipped files\n"
" -lL list differences; -L for brief\n"
" -f filename start viffing at this filename\n"
" skip: -i ignore differences in headers\n"
" -x exclude generated files\n"
" view: -m monocrome display\n"
" -t width set tabulator width\n"
" diff: -o option pass option to diff\n"
" -p program use program instead of "DIFFCMD"\n"
"examples:\n"
" viff *.c *.h makefile ../WORK\n"
" viff -vlx *.* ../WORK\n"
" viff -t 4 -o -b sved.c sved.bak\n");
exit(EXIT_SUCCESS);
}
int
message(const char *fmt, ...)
{
va_list varg;
int ch = 0;
if (report) {
if (!brief_report) {
va_start(varg, fmt);
vprintf(fmt, varg);
va_end(varg);
putchar('\n');
}
} else {
va_start(varg, fmt);
ch = vmessage(fmt, varg);
va_end(varg);
}
return ch;
}
static int
vmessage(const char *fmt, va_list varg)
{
char buf[1000], *p=buf;
int ch, i, len, dx;
*p++ = ' ';
p += vsprintf(p, fmt, varg);
p += sprintf(p, " (press a key)");
memset(p, ' ', COLS);
len = (int)(p-buf); dx = 0;
for (;;) {
attron(status_attr);
(void)move(VIEWLINES, 0);
(void)clrtoeol();
for (i = 0; i < COLS; i++) {
(void)addch(buf[dx+i]);
}
(void)standend();
(void)move(VIEWLINES, len-dx);
(void)refresh();
ch = getch();
switch (ch) {
case KEY_LEFT:
case KEYCTRL('B'): if (dx > 0) dx--; break;
case KEY_RIGHT:
case KEYCTRL('F'): if (dx+COLS < len) dx++; break;
case KEY_HOME:
case KEYCTRL('A'): dx = 0; break;
case KEY_END:
case KEYCTRL('E'): dx = MAX(0, len-COLS); break;
default:
(void)move(VIEWLINES, 0);
(void)clrtoeol();
(void)refresh();
if (ch == 'Q' && confirm_quit()) exit(EXIT_SUCCESS);
return ch;
}
}
}
BOOLEAN
input(int flag, char *buf, const char *fmt, ...)
{
va_list varg;
int inputpos;
int i, len, pos;
int ch;
char *p;
va_start(varg, fmt);
inputpos = vstatusline(0, fmt, varg)+1;
va_end(varg);
pos = len = strlen(buf);
(void)refresh();
for (;;) {
(void)move(VIEWLINES, inputpos);
(void)attron(status_attr);
for (i = 0; i < len; i++) {
(void)addch(buf[i]);
}
for (i = COLS-(inputpos+len); i-- > 0; ) {
(void)addch(' ');
}
(void)attroff(status_attr);
(void)move(VIEWLINES, inputpos+pos);
(void)refresh();
ch = getch();
if (KEYABORT(ch)) return FALSE;
if (KEYRETURN(ch)) break;
switch (ch) {
case KEY_LEFT:
case KEYCTRL('B'): if (pos > 0) pos--; break;
case KEY_RIGHT:
case KEYCTRL('F'): if (pos < len) pos++; break;
case KEY_HOME:
case KEYCTRL('A'): pos = 0; break;
case KEY_END:
case KEYCTRL('E'): pos = len; break;
case KEY_BACKSPACE:
case '\x08':
if (pos > 0) {
memmove(buf+pos-1, buf+pos, len-pos);
pos--; len--;
}
break;
case KEY_DELETE:
case KEY_DC:
case KEYCTRL('D'):
if (pos < len) {
memmove(buf+pos, buf+pos+1, len-pos-1);
len--;
}
break;
#ifdef KEY_EOL
case KEY_EOL:
#endif
case KEYCTRL('K'): len = pos; break;
default:
if ((flag & INPUT_NUMERIC) && !isdigit(ch)) break;
if (isspace(ch)) break;
if (inputpos+len < COLS-1 && PRINTABLE_CHAR(ch)) {
memmove(buf+pos+1, buf+pos, len-pos);
buf[pos] = (char)ch;
pos++; len++;
}
}
}
buf[len] = '\0';
while (isspace(*(unsigned char*)buf))
buf++;
for (p = buf+strlen(buf); p-- > buf; *p = '\0') {
if (!isspace(*(unsigned char*)p)) break;
}
if (buf[0] == '\0') return FALSE;
return TRUE;
}
int
ask(char *text[], int nlines, char *accept, char escape, const char *fmt, ...)
{
char buf[20];
va_list varg;
int xpos;
va_start(varg, fmt);
xpos = vstatusline(STATUS_HELP, fmt, varg);
va_end(varg);
(void)move(VIEWLINES, xpos);
xpos += sprintf(buf, " [%s]", accept)+1;
(void)attron(status_attr);
(void)addstr(buf);
(void)standend();
(void)move(VIEWLINES, xpos);
(void)refresh();
for (;;) {
int ch = getch();
if (KEYABORT(ch)) return escape;
if (strchr(accept, tolower(ch))) return tolower(ch);
if (ch == KEY_F(1)) {
help(text, nlines);
} else {
(void)beep();
}
}
/*NOTREACHED*/
}
char *
my_strerror(void)
{
static char buf[100];
int len;
strncpy(buf, strerror(errno), sizeof(buf)-1);
len = strlen(buf);
if (len > 0 && buf[len-1] == '\n') {
buf[len-1] = '\0';
}
return buf;
}
BOOLEAN
is_dir(char *fname)
{
struct stat st;
#if defined(MSDOS) || defined(WIN32)
if (isalpha(((unsigned char*)fname)[0]) &&
fname[1] == ':' && fname[2] == '\0') return TRUE;
#endif
return stat(fname, &st) == 0 && S_ISDIR(st.st_mode);
}
char *
make_path(char *buf, char *fname)
{
strcpy(buf, fname);
buf += strlen(buf);
if (buf[-1] != SEPCHAR /* (fname is from argv[]; not empty) */
#if defined(MSDOS) || defined(WIN32)
&& buf[-1] != ':' /* don't turn eg "a:" into "a:\" */
#endif
) *buf++ = SEPCHAR;
return buf;
}
char *
strip_path(char *fname)
{
char *p;
for (p = fname+strlen(fname); fname < p; p--) {
if (p[-1]=='\\' || p[-1]==':' || p[-1]=='/') break;
}
return p;
}
char *
lower_str(char *str)
{
#ifdef MSDOS
char *p;
for (p = str; *p; p++) {
*p = (char)tolower(*(unsigned char*)p);
}
#endif
return str;
}
BOOLEAN
find_diffcmd(void)
{
char *path, *p;
p = strip_path(diffcmd);
if (diffcmd < p) { /* a path is given; check there */
return find_diffcmd_do("");
} else {
#ifndef UNIX
if (find_diffcmd_do(".")) return TRUE;
#endif
if ((path=getenv("PATH")) == NULL) return FALSE;
path = xstrdup(path);
for (p = strtok(path, PATHSEP); p; p = strtok(NULL, PATHSEP)) {
if (find_diffcmd_do(p)) break;
}
free(path);
return p ? TRUE : FALSE;
}
}
static BOOLEAN
find_diffcmd_do(char *path)
{
char buf[FILENAME_MAX+100], *p; /* +n; don't push our luck... */
struct stat st;
p = buf;
strcpy(p, path); p += strlen(p);
if (buf < p && p[-1] != SEPCHAR) *p++ = SEPCHAR;
strcpy(p, diffcmd);
#if defined(MSDOS) || defined(WIN32)
{ int len=strlen(p);
p += strlen(p);
if (len<4 || stricmp(p-4, ".exe")!=0) {
strcpy(p, ".exe");
}
if (stat(buf, &st) == 0) {
free(diffcmd); diffcmd = xstrdup(buf);
return TRUE;
}
if (len<4 || stricmp(p-4, ".com")!=0) {
strcpy(p, ".com");
}
}
#endif
if (stat(buf, &st) == 0) {
free(diffcmd); diffcmd = xstrdup(buf);
return TRUE;
}
return FALSE;
}
void
construct_argv(char *dir1, char *dir2, int *argcp, char ***argvp)
{
int i, argc=0;
size_t fnamelen=0;
char **argv=xmalloc((WILD_MAXARGC+1)*sizeof(char**));
char *fnames=xmalloc(WILD_MAXARGV+FILENAME_MAX+1);
char *fname;
#ifdef BC31
struct ffblk ffblk;
char dirbuf[FILENAME_MAX+50];
int res;
strcpy(make_path(dirbuf, dir1), "*.*");
for (res = findfirst(dirbuf, &ffblk, 0); res==0; res = findnext(&ffblk)) {
fname = ffblk.ff_name;
#elif defined(MSVC)
struct _find_t findt;
char dirbuf[FILENAME_MAX+50];
unsigned res;
strcpy(make_path(dirbuf, dir1), "*.*");
for (res = _dos_findfirst(dirbuf, _A_RDONLY|_A_SYSTEM, &findt);
res == 0; res = _dos_findnext(&findt)) {
fname = findt.name;
#else
DIR *dirp;
struct dirent *dirent;
dirp = opendir(dir1);
if (dirp == NULL) error("cannot read directory '%s'", dir1);
while ((dirent=readdir(dirp)) != NULL) {
fname = dirent->d_name;
#endif
if (strcmp(fname, ".")==0 || strcmp(fname, "..")==0) continue;
if (argc == WILD_MAXARGC) {
error("more than %d files in %s", argc, dir1);
}
if (fnamelen >= WILD_MAXARGV-(FILENAME_MAX+1)) {
error("total filename lengths in %s exceeds %lu bytes",
dir1, (long unsigned)fnamelen);
}
strcpy(make_path(fnames+fnamelen, dir1), fname);
argv[argc++] = (char*)fnamelen; /* an offset; adjust later */
fnamelen += strlen(fnames+fnamelen)+1;
}
/* as the last member of argv[], add the directory to viff against */
argv[argc++] = (char*)fnamelen; /* an offset; adjust later */
strcpy(fnames+fnamelen, dir2);
fnamelen += strlen(dir2)+1;
/* now compact the two tables (argc,fnamelen are both >0 because of dir2) */
constructed.argv = argv = xrealloc(argv, argc * sizeof(char**));
constructed.fnames = fnames = xrealloc(fnames, fnamelen);
for (i = 0; i < argc; i++) { /* realloc(fnames) done; adjust now */
argv[i] = fnames + (size_t)(long)argv[i];
}
*argcp = argc;
*argvp = constructed.argv;
atexit(free_constructed);
}
static void
free_constructed(void)
{
free(constructed.argv);
free(constructed.fnames);
}
void
skip_directories(int from, int to, char *argv[])
{
int i;
for (i = from; i < to; i++) {
if (argv[i] == NULL) continue;
if (is_dir(argv[i])) {
argv[i] = NULL;
}
}
}
void
skip_generated_files(int from, int to, char *argv[])
{
GENERATED_FILE *g;
char fnamebuf[FILENAME_MAX];
char *fname;
int len;
int skipped;
int i, j;
/* compare fname's extension with those in g->skipext
if found then
put "fname.ifext" into fnamebuf
look through all non-processes files again and compare it to fnamebuf
if found then
this means that the file being tested should be skipped
*/
skipped = 0;
for (i = from; i < to; i++) {
if (argv[i] == NULL) continue;
fname = argv[i];
len = strlen(fname);
for (g = generated_files; g->skipext; g++) {
if (g->len <= len && strcmp(fname+len-g->len, g->skipext)==0) {
strcpy(fnamebuf, strip_path(fname));
strcpy(strchr(fnamebuf, '.'), g->ifext); /* we know there's a . */
for (j = from; j < to; j++) {
if (argv[j] == NULL) continue;
if (strcmp(fnamebuf, strip_path(argv[j])) == 0) {
if (verbose) message("skipping generated file %s",fname);
argv[i] = NULL;
skipped++;
break;
}
}
break;
}
}
}
if (!verbose && skipped>0) {
message("skipping %d generated file%s", skipped, skipped==1 ? "" : "s");
}
}
void
skip_binary_files(int from, int to, char *argv[])
{
BINARY_EXT *be;
char **b;
int i, len, skipped;
skipped = 0;
for (i = from; i < to; i++) {
if (argv[i] == NULL) continue;
for (b = binary_files; *b; b++) {
if (strcmp(*b, argv[i]) == 0) {
if (verbose) message("skipping binary file %s", argv[i]);
argv[i] = NULL;
skipped++;
break;
}
}
if (argv[i] == NULL) continue;
len = strlen(argv[i]);
for (be = binary_ext; be->len > 0; be++) {
if (be->len <= len && strcmp(argv[i]+len-be->len, be->ext)==0) {
if (verbose) message("skipping binary file %s", argv[i]);
argv[i] = NULL;
skipped++;
break;
}
}
}
if (!verbose && skipped > 0) {
message("skipping %d binary file%s", skipped, skipped==1 ? "" : "s");
}
}
#ifdef MALLORY_FLAG
#undef xmalloc
#undef xrealloc
#undef xstrdup
#endif
void *
xmalloc(size_t size)
{
void *ptr = malloc(size==0 ? 1 : size); /* turn malloc(0) into malloc(1) */
if (ptr == NULL) {
panic("malloc(%lu) failed", (long unsigned)size);
}
return ptr;
}
void *
xrealloc(void *ptr, size_t size)
{
void *newptr = realloc(ptr, size);
if (newptr == NULL) {
panic("realloc(p,%lu) failed", (long unsigned)size);
}
return newptr;
}
char *
xstrdup(char *str)
{
return strcpy(xmalloc(strlen(str)+1), str);
}